//==============================================================================
//
//             DSource - the source class in the ofc-library
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2006-07-22 13:28:57 $ $Revision: 1.8 $
//
//==============================================================================

#include "ofc/DSource.h"

#include <stdio.h>


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DText.h"

#include "ofc/DTextReadable.h"


//
// The DSource class is used by the @class(DLexer) and the @class(DTokenizer) for 
// storing the relevant info of the source for the lexer c.q. tokenizer. By storing
// the objects in a list, a stack of sources can be implemented.
// 

@interface DSource : Object
{
@private
  id <DTextReadable>  _source;   // the reference to the source (file)
  DText              *_name;     // the name of the source (file)
  DText              *_line;     // the read line 
  int                 _lineNr;   // the line number
  int                 _columnNr; // the column number
  int                 _scanned;  // the scanned part of _line
}

#endif


@implementation DSource

  
//// Constructors

//
// Initialise an empty source object
//
// @return the object
//

- (DSource *) init 
{
  [super init];
  
  _source   = nil;
  _name     = [[DText alloc] init];
  _line     = nil;
  _lineNr   = 0;
  _columnNr = 0;
  _scanned  = 0;
   
  return self;
}

//
// Initialise a source object with a source
//
// @param source   the reference to the file
// @param name     the name of the file
// 
// @return the object
//

- (DSource *) init :(id <DTextReadable>) source :(const char *) name
{
  [self init];
  
  [self set :source :name];
  
  return self;
}


//// Copy related methods

//
// Do a shallow  copy of the object (not implemented)
//
// @return the object
//
- shallowCopy
{
  WARNING(DW_METHOD_NOT_IMPL,"shallowCopy");
  
  return [super shallowCopy];
}


//// Deconstructor

//
// Free the object
//
// @return the object
//

- free
{
  [_name free];

  if (_line != nil)
    [_line free];
  
  return [super free];
}


//// Member methods

//
// Get the name of the source
// 
// @return a (new) DText with the name
// 

- (const char *) name
{
  return [_name cstring];
}

//
// Get the remaining line of text
// 
// @return the remaining line of text
// 

- (const char *) line
{
  return [_line cstring] + _scanned;
}

//
// Get the current line number
// 
// @return the line number
// 

- (int) lineNumber
{
  return _lineNr;
}

//
// Get the current column number
// 
// @return the column number
// 

- (int) columnNumber
{
  return _columnNr+1;
}


//// Main methods

//
// Set the source definition
//
// @param source   the reference to the file
// @param name     the name of the file
// 
// @return success
//

- (BOOL) set :(id <DTextReadable>) source :(const char *) name
{
  BOOL ok = YES;
  
  [_name set :name];

  _source   = source;

  _lineNr   = 0;
  _columnNr = 0;
  _scanned  = 0;

  ok = [self nextLine];
  
  return ok;
}

//
// Move to the next line
// 
// @return success
// 

- (BOOL) nextLine
{
  while (![_source isEof])
  {
    if (_line != nil)
      [_line free];
  
    _line = [_source readLine];
  
    _columnNr = 0;
    _scanned  = 0;
    _lineNr++;
    
    if (![self isLineScanned])
    {
      return YES;
    }
  }

  return NO;
}

//
// Append the next line to the current line
// 
// @return success
// 
- (BOOL) appendLine
{
  while (![_source isEof])
  {
    DText *extra = [_source readLine];
    
    if (extra != nil)
    {
      [_line append :[extra cstring]];

      _columnNr = 0;
      _lineNr++;
      
      [extra free];
    }
    
    if (![self isLineScanned])
    {
      return YES;
    }
  }
  
  return NO;
}
      
//
// Indicate that a part of the line is scanned
// 
// @param length   the length of the scanned part
// 
// @return is the whole line scanned ?
// 

- (BOOL) scanned :(unsigned) length
{
  _columnNr += length;
  _scanned  += length;
  
  return (_scanned >= [_line length]);
}

//
// Check if the current line is fully scanned
// 
// @return is it ?
// 

- (BOOL) isLineScanned
{
  return (_scanned >= [_line length]);
}

//
// Generate an error on stderr for the current file and line
// 
// @param msg      the error message
// 
// @return the object
// 

- (DSource *) error :(const char *) msg
{
  if (msg == NULL)
  {
    WARNING(DW_INVALID_ARG, "msg");
  }
  else
  {
    fprintf(stderr, "%s, line %d: %s\n", [_name cstring], _lineNr, msg);
  }
  
  return self;
}

@end


/*==========================================================================*/
